package cn.cubix.flow.controller.workflow;

import cn.cubix.flow.controller.dto.workflow.ProcessInstDto;
import cn.cubix.flow.service.ImageService;
import cn.cubix.flow.util.Result;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.RuntimeServiceImpl;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.image.ProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/api/workflow/processinst")
@RequiredArgsConstructor(onConstructor=@__({@Autowired,@Lazy}))
public class ProcessInstApi {

    private final RuntimeServiceImpl runtimeService;
    private final RepositoryService repositoryService;
    private final ProcessEngine processEngine;
    private final ImageService imageService;

    /** 分页查询流程实例 */
    @PostMapping(value = "/page",consumes = {MediaType.APPLICATION_JSON_VALUE})
    @PreAuthorize("hasAnyAuthority('ADMIN','API_WORKFLOW_PROCESSINST_PAGE')")
    public Object processInst(@RequestBody JSONObject body){
        long current = 1L, size = 10L, total = 0L, start = 0L;
        if(body.containsKey("page")){
            JSONObject page = body.getJSONObject("page");
            current = page.getInt("current");
            size = page.getInt("size");
            start = (current-1)*size;
        }
        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        List<ProcessInstance> processInstanceList = query.orderByStartTime().desc().listPage((int)start,(int)size);
        List<ProcessInstDto> records = processInstanceList.stream().map(ProcessInstDto::from).collect(Collectors.toList());
        return Result.SUCCESS(records,"查询成功").setPage(current,size,query.count());
    }

    /** 回退流程实例 */
    @PostMapping(value="/rollback",consumes = {MediaType.APPLICATION_JSON_VALUE})
    @PreAuthorize("hasAnyAuthority('ADMIN','API_WORKFLOW_PROCESSINST_ROLLBACK')")
    public Object rollback(@RequestBody JSONObject body){
        String processInstanceId = body.getStr("processInstanceId");
        String currentTaskKey = body.getStr("currentTaskKey");
        String targetTaskKey = body.getStr("targetTaskKey");

        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(processInstanceId)
                .moveActivityIdTo(currentTaskKey, targetTaskKey)
                .changeState();

        JSONObject result = new JSONObject(true);
        result.set("success",true);
        return result;
    }

    /** 查看流程图片 */
    @GetMapping(value = "/image/{processId}",produces = MediaType.IMAGE_PNG_VALUE)
    @PreAuthorize("hasAnyAuthority('ADMIN','API_WORKFLOW_PROCESSINST_IMAGE')")
    public void showPic(HttpServletResponse resp, @PathVariable("processId") String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (pi == null) {
            return;
        }
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(processId)
                .list();

        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }

        /**
         * 生成流程图
         */
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, false);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = resp.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    /** 查看流程图片2 */
    @ResponseBody
    @GetMapping(value = "/image2/{processId}", produces = MediaType.IMAGE_PNG_VALUE)
    @PreAuthorize("hasAnyAuthority('ADMIN','API_WORKFLOW_PROCESSINST_IMAGE2')")
    public byte[] showPic2(HttpServletResponse resp, @PathVariable("processId") String processId) throws Exception {
        return imageService.generateImageByProcInstId(processId);
    }

    /** 挂起流程实例 */
    @PostMapping(value = "/suspend",consumes = {MediaType.APPLICATION_JSON_VALUE})
    @PreAuthorize("hasAnyAuthority('ADMIN','API_WORKFLOW_PROCESSINST_SUSPEND')")
    public Object suspend(@RequestBody JSONObject body){
        Result<Object> result = Result.FAILURE("流程实例挂起失败");
        String processInstanceId = body.getStr("processInstanceId");
        if(!StrUtil.isEmpty(processInstanceId)) {
            runtimeService.suspendProcessInstanceById(processInstanceId);
            result.toSuccess().setMessage("流程实例挂起成功");
        }
        return result;
    }

    /** 激活流程实例 */
    @PostMapping(value = "/activate",consumes = {MediaType.APPLICATION_JSON_VALUE})
    @PreAuthorize("hasAnyAuthority('ADMIN','API_WORKFLOW_PROCESSINST_ACTIVATE')")
    public Object activate(@RequestBody JSONObject body){
        Result<Object> result = Result.FAILURE("流程实例挂起失败");
        String processInstanceId = body.getStr("processInstanceId");
        if(!StrUtil.isEmpty(processInstanceId)) {
            runtimeService.activateProcessInstanceById(processInstanceId);
            result.toSuccess().setMessage("流程实例挂起成功");
        }
        return result;
    }
}
